using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Threading;

// @11:04 2009/09/24

namespace ReadBKF2 {
    public class BkfParser {
        public List<DBLk> aldblk = new List<DBLk>();

        public BkfParser(FileStream fs) {
            while (fs.Position < fs.Length) {
                MTF_DB_HDR hdr = Serut.ReadCBH(fs);

                Console.WriteLine("" + hdr.DBLKName);

                if (hdr.DBLKName == "SFMB") {
                    MTF_SFMB vSfmb = Serut.ReadSfmb(fs);
                    Serut.EventBlk(fs, hdr);
                    continue;
                }

                DBLk dblk = new DBLk();
                dblk.hdr = hdr;
                dblk.alsh = ParseEvent(fs, hdr);
                aldblk.Add(dblk);
            }
        }

        List<MTF_STREAM_HDR> ParseEvent(FileStream fs, MTF_DB_HDR hdr) {
            Serut.EventBlk(fs, hdr);
            List<MTF_STREAM_HDR> alsh = new List<MTF_STREAM_HDR>();
            while (true) {
                MTF_STREAM_HDR hdr2 = Serut.ReadSH(fs);
                Console.WriteLine(" " + hdr2.StreamIDText);
                fs.Seek(Convert.ToInt64(hdr2.StreamLength), SeekOrigin.Current);
                Serut.Align(fs);
                if (hdr2.StreamIDText == "SPAD") break;
                alsh.Add(hdr2);
            }
            return alsh;
        }
    }

    public class BkfFastLocateSfmb {
        public static List<Int64> Parse(FileStream fs) {
            BinaryReader br = new BinaryReader(fs);
            List<Int64> aloff = new List<Int64>();
            fs.Position = 0;
            uint DBLKType0 = br.ReadUInt32();
            if (DBLKType0 == 1162887508) { // TAPE
                fs.Position = 84;
                ushort FormatLogicalBlockSize = br.ReadUInt16();
                fs.Position = fs.Length - FormatLogicalBlockSize;
                uint DBLKType1 = br.ReadUInt32();
                if (DBLKType1 == 1112360531) { // SFMB
                    fs.Position = fs.Length - FormatLogicalBlockSize;
                    MTF_DB_HDR hdr = Serut.ReadCBH(fs);
                    MTF_SFMB vSfmb = Serut.ReadSfmb(fs);
                    foreach (uint v in vSfmb.PBAofPreviousFilemarksArray) {
                        aloff.Insert(0, Convert.ToInt64(FormatLogicalBlockSize) * v);
                    }
                    return aloff;
                }
            }
            throw new NotSupportedException();
        }
    }

    public class BkfOrdLocateSfmb {
        public static List<Int64> Parse(FileStream ofs) {
            List<Int64> aloff = new List<Int64>();
            BufferedStream fs = new BufferedStream(ofs, 64);
            BinaryReader br = new BinaryReader(fs);
            int snap = 1024;
            while (fs.Position < fs.Length) {
                Int64 off = fs.Position;
                uint DBLKType = br.ReadUInt32();
                string DBLKTypeText = Ut.ID2Name(DBLKType);
                br.ReadUInt32();
                ushort OffsetToFirstEvent = br.ReadUInt16();

                if (DBLKType == 1162887508) { // TAPE
                    fs.Position = off + 84;
                    snap = br.ReadUInt16();
                }

                fs.Position = Ut.Snap4(off + OffsetToFirstEvent);

                if (DBLKType == 1112360531) { // SFMB
                    aloff.Add(off);
                    continue;
                }

                while (true) {
                    Int64 offs = fs.Position;
                    uint StreamID = br.ReadUInt32();
                    string StreamIDText = Ut.ID2Name(StreamID);
                    br.ReadUInt16();
                    br.ReadUInt16();
                    Int64 StreamLength = Convert.ToInt64(br.ReadUInt64());

                    fs.Position = Ut.Snap4(offs + 22 + StreamLength);

                    if (StreamID == 1145131091) break; // SPAD
                }

                Ut.Snap(fs, snap);
            }
            return aloff;
        }

        class Ut {
            public static string ID2Name(uint ty) {
                return Encoding.ASCII.GetString(new byte[] { (byte)(ty >> 0), (byte)(ty >> 8), (byte)(ty >> 16), (byte)(ty >> 24), });
            }
            public static Int64 Snap4(Int64 off) {
                byte b = (byte)(((byte)off) & 3);
                if (b == 0) return off;
                return off + (4 - b);
            }
            public static void Snap(Stream fs, int snap) {
                Int64 pos = fs.Position;
                int size = (int)(pos % snap);
                if (size != 0) pos += snap - size;
                fs.Position = pos;
            }
        }
    }

    public class BkfReadSet {
        public SetDesc descSet = null;
        public BV[] albv = null;
        public K k = K.None;

        [Flags]
        public enum K {
            None = 0, SSet = 1, ESet = 2,
            All = 3,
        }

        public BkfReadSet(FileStream fs, Int64 offSfmb, K readSet) {
            fs.Position = offSfmb;
            MTF_DB_HDR hdr1 = Serut.ReadCBH(fs);
            if (hdr1.DBLKName != "SFMB")
                throw new InvalidDataException();
            Serut.EventBlk(fs, hdr1);
            MTF_DB_HDR hdr2 = Serut.ReadCBH(fs);
            if (hdr2.DBLKName == "SSET") {
                if (0 != (readSet & K.SSet)) {
                    SetDesc p = new SetDesc();
                    p.sset = Serut.ReadSSet(fs);
                    p.DataSetName = Serut2.ReadStr(fs, hdr2, p.sset.DataSetName);
                    p.SetDescription = Serut2.ReadStr(fs, hdr2, p.sset.SetDescription);
                    p.DataSetPassword = Serut2.ReadStr(fs, hdr2, p.sset.DataSetPassword);
                    p.UserName = Serut2.ReadStr(fs, hdr2, p.sset.UserName);
                    descSet = p;
                }
                k = K.SSet;
            }
            else if (hdr2.DBLKName == "ESET") {
                if (0 != (readSet & K.ESet)) {
                    Serut.EventBlk(fs, hdr2);
                    while (true) {
                        MTF_STREAM_HDR sh = Serut.ReadSH(fs);
                        if (sh.StreamIDText == "SPAD") {
                            break;
                        }
                        if (sh.StreamIDText == "TFDD") {
                            MemoryStream si = new MemoryStream(new BinaryReader(fs).ReadBytes(Convert.ToInt32(sh.StreamLength)), false);
                            BV bv = null;
                            BD bd = null;
                            List<BV> albv = new List<BV>();
                            while (si.Position < si.Length) {
                                MTF_FDD_HDR fddhdr = Serut.ReadfddHDR(si);
                                switch (fddhdr.TYPEText) {
                                    case "VOLB":
                                        MTF_FDD_VOLB volb = Serut.readfddVolb(si);
                                        albv.Add(bv = new BV(
                                            Serut2.ReadStr(si, fddhdr, volb.DeviceName)
                                            ));
                                        break;
                                    case "DIRB":
                                        MTF_FDD_DIRB dirb = Serut.readfddDirb(si);
                                        if (bv != null)
                                            bv.albd.Add(bd = new BD(
                                                Serut2.ReadStr(si, fddhdr, dirb.DirectoryName).Replace('\0', '\\')
                                                ));
                                        break;
                                    case "FILE":
                                        MTF_FDD_FILE file = Serut.readfddFile(si);
                                        if (bd != null)
                                            bd.albf.Add(new BF(
                                                Serut2.ReadStr(si, fddhdr, file.FileName),
                                                file.LastModificationDate
                                                ));
                                        break;
                                    case "FEND":
                                        break;
                                    default:
                                        //throw new InvalidDataException();
                                        break;
                                }
                                si.Position = fddhdr.offset + fddhdr.LENGTH;
                                if (fddhdr.TYPEText == "FEND") break;
                            }
                            this.albv = albv.ToArray();
                        }
                        Serut.NextSH(fs, sh);
                    }
                }
                k = K.ESet;
            }
        }
    }

    public class SetDesc {
        public MTF_SSET sset;
        public String DataSetName, SetDescription, DataSetPassword, UserName;
    }

    public class BkfEntity {
        public static List<BV> Parse(FileStream fs) {
            BV bv = null;
            BD bd = null;
            List<BV> albv = new List<BV>();
            while (fs.Position < fs.Length) {
                MTF_DB_HDR hdr = Serut.ReadCBH(fs);

                if (hdr.DBLKName == "TAPE") {
                    MTF_TAPE vTape = Serut.ReadTape(fs);
                }
                if (hdr.DBLKName == "SFMB") {
                    MTF_SFMB vSfmb = Serut.ReadSfmb(fs);
                    Serut.EventBlk(fs, hdr);
                    continue;
                }

                switch (hdr.DBLKName) {
                    case "VOLB": {
                            MTF_VOLB volb = Serut.ReadVolb(fs);
                            String DeviceName = Serut2.ReadStr(fs, hdr, volb.DeviceName);
                            albv.Add(bv = new BV(DeviceName));
                            bd = null;
                            break;
                        }
                    case "DIRB": {
                            if (bv != null) {
                                MTF_DIRB dirb = Serut.ReadDirb(fs);
                                String DirectoryName = Serut2.ReadStr(fs, hdr, dirb.DirectoryName).Replace('\0', '\\');
                                bd = new BD(DirectoryName);
                                bv.albd.Add(bd);
                            }
                            break;
                        }
                    case "FILE": {
                            if (bd != null) {
                                MTF_FILE file = Serut.ReadFile(fs);
                                String FileName = Serut2.ReadStr(fs, hdr, file.FileName);
                                BF bf = new BF(FileName, file.LastModificationDate);
                                bd.albf.Add(bf);
                            }
                            break;
                        }
                }

                Serut3.ParseEvent(fs, hdr);
            }
            return albv;
        }
    }

    public interface IEntityCb {
        void SelBV(String DeviceName);
        void SelBD(String DirectoryName);
        void SelBF(String FileName, DateTime LastModificationDate, Int64 cbStan);
    }

    public class BkfEntityCb {
        public static void Expose(FileStream fs, IEntityCb pfn) {
            String FileName = null;
            DateTime LastModificationDate = DateTime.MinValue;

            while (fs.Position < fs.Length) {
                MTF_DB_HDR hdr = Serut.ReadCBH(fs);

                if (hdr.DBLKName == "TAPE") {
                    MTF_TAPE vTape = Serut.ReadTape(fs);
                }
                if (hdr.DBLKName == "SFMB") {
                    MTF_SFMB vSfmb = Serut.ReadSfmb(fs);
                    Serut.EventBlk(fs, hdr);
                    continue;
                }

                switch (hdr.DBLKName) {
                    case "VOLB": {
                            MTF_VOLB volb = Serut.ReadVolb(fs);
                            String DeviceName = Serut2.ReadStr(fs, hdr, volb.DeviceName);
                            pfn.SelBV(DeviceName);
                            break;
                        }
                    case "DIRB": {
                            MTF_DIRB dirb = Serut.ReadDirb(fs);
                            String DirectoryName = Serut2.ReadStr(fs, hdr, dirb.DirectoryName).Replace('\0', '\\');
                            pfn.SelBD(DirectoryName);
                            break;
                        }
                    case "FILE": {
                            MTF_FILE file = Serut.ReadFile(fs);
                            FileName = Serut2.ReadStr(fs, hdr, file.FileName);
                            LastModificationDate = file.LastModificationDate;
                            break;
                        }
                }

                Int64 cbStan = 0;
                foreach (MTF_STREAM_HDR sh in Serut3.ParseEvent(fs, hdr)) {
                    if (sh.StreamIDText == "STAN") {
                        cbStan = Convert.ToInt64(sh.StreamLength);
                    }
                }

                switch (hdr.DBLKName) {
                    case "FILE": {
                            pfn.SelBF(FileName, LastModificationDate, cbStan);
                            break;
                        }
                }
            }
            return;
        }
    }

    public class ExposeSelDirs : IExposeSelector {
        BV bvcur = null;
        BD[] albdSel = null;
        String baseDir;

        public ExposeSelDirs(String baseDir, BV bvcur, BD[] albdSel) {
            this.baseDir = baseDir;
            this.bvcur = bvcur;
            this.albdSel = albdSel;
        }

        String curbdDir = null;
        BF bfMatched = null;

        public bool SelBV(BV bv) {
            return bv.DeviceName.Equals(bvcur.DeviceName);
        }
        public bool SelBD(BD bd) {
            foreach (BD bd1 in albdSel) {
                if (bd.DirectoryName.Equals(bd1.DirectoryName)) {
                    Directory.CreateDirectory(curbdDir = Path.Combine(baseDir, bd.DirectoryName));
                    return true;
                }
            }
            return false;
        }
        public bool SelBF(BF bf) {
            bfMatched = bf;
            return true;
        }
        public string Getfp() {
            return Path.Combine(curbdDir, bfMatched.FileName);
        }
    }

    public class ExposeSelfs : IExposeSelector {
        BV bvcur;
        BD bdcur;
        BF[] albfSel;
        String baseDir;

        public ExposeSelfs(String baseDir, BV bvcur, BD bdcur, BF[] albfSel) {
            this.baseDir = baseDir;
            this.bvcur = bvcur;
            this.bdcur = bdcur;
            this.albfSel = albfSel;
        }

        BF bfMatched = null;

        public bool SelBV(BV bv) {
            return bv.DeviceName.Equals(bvcur.DeviceName);
        }
        public bool SelBD(BD bd) {
            if (bd.DirectoryName.Equals(bdcur.DirectoryName)) {
                return true;
            }
            return false;
        }
        public bool SelBF(BF bf) {
            foreach (BF bf1 in albfSel) {
                if (bf1.FileName.Equals(bf.FileName)) {
                    bfMatched = bf;
                    return true;
                }
            }
            return false;
        }
        public string Getfp() {
            return Path.Combine(baseDir, bfMatched.FileName);
        }
    }

    public interface IExposeSelector {
        bool SelBV(BV bv);
        bool SelBD(BD bd);
        bool SelBF(BF bf);
        String Getfp();
    }

    public class PR {
        public Int64 pos;
        public BV bv;
        public BD bd;
        public BF bf;

        public PR(Int64 pos, BV bv, BD bd, BF bf) {
            this.pos = pos;
            this.bv = bv;
            this.bd = bd;
            this.bf = bf;
        }
    }

    public interface IPR {
        void Report(PR pr);
    }

    public class BkfExpose {
        static WaitHandle evNever = new AutoResetEvent(false);

        public static void Walk(FileStream fs, Int64 pos, IExposeSelector pcb, WaitHandle evStop, IPR pr, WaitHandle evPR) {
            fs.Position = pos;
            BV bv = null;
            BD bd = null;
            BF bf = null;
            MTF_FILE file = null;
            if (evStop == null) evStop = evNever;
            if (evPR == null) evPR = evNever;
            while (!evStop.WaitOne(0, false)) {
                MTF_DB_HDR hdr = Serut.ReadCBH(fs);

                if (hdr.DBLKName == "SFMB") {
                    MTF_SFMB vSfmb = Serut.ReadSfmb(fs);
                    Serut.EventBlk(fs, hdr);
                    continue;
                }
                else if (hdr.DBLKName == "ESET") {
                    break;
                }

                switch (hdr.DBLKName) {
                    case "VOLB": {
                            MTF_VOLB volb = Serut.ReadVolb(fs);
                            String DeviceName = Serut2.ReadStr(fs, hdr, volb.DeviceName);
                            bv = new BV(DeviceName);
                            if (!pcb.SelBV(bv)) bv = null;
                            bd = null;
                            bf = null;
                            break;
                        }
                    case "DIRB": {
                            if (bv != null) {
                                MTF_DIRB dirb = Serut.ReadDirb(fs);
                                String DirectoryName = Serut2.ReadStr(fs, hdr, dirb.DirectoryName).Replace('\0', '\\');
                                bd = new BD(DirectoryName);
                                bf = null;
                                if (!pcb.SelBD(bd)) bd = null;
                            }
                            break;
                        }
                    case "FILE": {
                            if (bd != null) {
                                file = Serut.ReadFile(fs);
                                String FileName = Serut2.ReadStr(fs, hdr, file.FileName);
                                bf = new BF(FileName, file.LastModificationDate);
                                if (!pcb.SelBF(bf)) bf = null;
                            }
                            break;
                        }
                }

                List<MTF_STREAM_HDR> alsh = Serut3.ParseEvent(fs, hdr);

                Int64 lastoff = fs.Position;

                if (evPR.WaitOne(0, false)) pr.Report(new PR(lastoff, bv, bd, bf));

                if (hdr.DBLKName == "FILE" && bd != null && bf != null) {
                    String fp = pcb.Getfp();
                    using (FileStream os = File.Create(fp)) {
                        for (int x = 0; x < alsh.Count; x++) {
                            MTF_STREAM_HDR sh = alsh[x];
                            if (sh.StreamIDText == "STAN") {
                                fs.Position = sh.offsetStream;
                                StUt.Copy(fs, sh.StreamLength, os);
                            }
                        }
                    }
                    File.SetLastWriteTime(fp, bf.LastModificationDate);
                    //File.SetAttributes(fp, 0
                    //| ((0 != (file.FILEAtts & FILEAtts.HIDDEN_BIT)) ? FileAttributes.Hidden : 0)
                    //| ((0 != (file.FILEAtts & FILEAtts.READ_ONLY_BIT)) ? FileAttributes.ReadOnly : 0)
                    //| ((0 != (file.FILEAtts & FILEAtts.SYSTEM_BIT)) ? FileAttributes.System : 0)
                    //);
                }

                fs.Position = lastoff;
            }
            return;
        }

        class StUt {
            public static void Copy(Stream fsrc, UInt64 size, Stream fdst) {
                byte[] bin = new byte[4096];
                while (true) {
                    int cb = (int)Math.Min(4096ul, size);
                    if (cb == 0) break;
                    if (fsrc.Read(bin, 0, cb) != cb)
                        throw new EndOfStreamException();
                    fdst.Write(bin, 0, cb);
                    size -= (ushort)cb;
                }
            }
        }
    }

    public class BV {
        public string DeviceName;

        public BV(string DeviceName) {
            this.DeviceName = DeviceName;
        }

        public List<BD> albd = new List<BD>();
    }

    public class BD {
        public string DirectoryName;
        public uint DirectoryID = 0;

        public BD(string DirectoryName) {
            this.DirectoryName = DirectoryName;
        }

        public List<BF> albf = new List<BF>();
    }

    public class BF {
        public string FileName;
        public DateTime LastModificationDate;
        public uint FileID = 0;

        public BF(string FileName, DateTime LastModificationDate) {
            this.FileName = FileName;
            this.LastModificationDate = LastModificationDate;
        }
    }

    class Serut3 {
        public static List<MTF_STREAM_HDR> ParseEvent(FileStream fs, MTF_DB_HDR hdr) {
            Serut.EventBlk(fs, hdr);
            List<MTF_STREAM_HDR> alsh = new List<MTF_STREAM_HDR>();
            while (true) {
                MTF_STREAM_HDR hdr2 = Serut.ReadSH(fs);
                fs.Seek(Convert.ToInt64(hdr2.StreamLength), SeekOrigin.Current);
                Serut.Align(fs);
                if (hdr2.StreamIDText == "SPAD") break;
                alsh.Add(hdr2);
            }
            return alsh;
        }
    }
    class Serut2 {
        public static string ReadStr(FileStream fs, MTF_DB_HDR hdr, uint addr) {
            int size = ((ushort)(addr));
            int offset = (ushort)(addr >> 16);
            fs.Position = hdr.offset + offset;
            byte[] bin = new byte[size];
            if (size != fs.Read(bin, 0, size))
                throw new EndOfStreamException();
            switch (hdr.Strty) {
                case Strty.ANSI:
                case Strty.None:
                default:
                    return Encoding.GetEncoding(932).GetString(bin);
                case Strty.UNICODE:
                    return Encoding.Unicode.GetString(bin);
            }
        }
        public static string ReadStr(Stream fs, MTF_FDD_HDR hdr, uint addr) {
            int size = ((ushort)(addr));
            int offset = (ushort)(addr >> 16);
            fs.Position = hdr.offset + offset;
            byte[] bin = new byte[size];
            if (size != fs.Read(bin, 0, size))
                throw new EndOfStreamException();
            switch (hdr.Strty) {
                case Strty.ANSI:
                case Strty.None:
                default:
                    return Encoding.GetEncoding(932).GetString(bin);
                case Strty.UNICODE:
                    return Encoding.Unicode.GetString(bin);
            }
        }
    }
    class Serut {
        public static void Align(Stream si) {
            int p = (int)si.Position;
            if (0 == (p & 3)) return;
            si.Position += 4 - (p & 3);
        }
        public static void NextSH(Stream si, MTF_STREAM_HDR hdr) {
            Int64 pos = hdr.offsetStream + Convert.ToInt64(hdr.StreamLength);
            byte b = (byte)(((byte)pos) & 3);
            if (b != 0) pos += 4 - b;
            si.Position = pos;
        }
        public static void EventBlk(Stream si, MTF_DB_HDR hdr) {
            si.Position = (hdr.offset + hdr.OffsetToFirstEvent);
            Serut.Align(si);
        }
        public static void NextBlk(Stream si, MTF_TAPE o, MTF_DB_HDR hdr) {
            Int64 pos = (si.Position + o.FormatLogicalBlockSize + Convert.ToInt64(hdr.DisplayableSize));
            pos = pos - (pos % o.FormatLogicalBlockSize);
            si.Position = pos;
            Serut.Align(si);
        }
        public static MTF_TAPE ReadTape(Stream si) {
            MTF_TAPE o = new MTF_TAPE();
            BinaryReader br = new BinaryReader(si);
            o.offset = si.Position;
            o.MediaFamilyID = br.ReadUInt32();
            o.TAPEAttributes = br.ReadUInt32();
            o.MediaSequenceNumber = br.ReadUInt16();
            o.PasswordEncryptionAlgorithm = br.ReadUInt16();
            o.SoftFilemarkBlockSize = br.ReadUInt16();
            o.MediaBasedCatalogType = br.ReadUInt16();
            o.MediaName = br.ReadUInt32();
            o.MediaDescriptionMediaLabel = br.ReadUInt32();
            o.MediaPassword = br.ReadUInt32();
            o.SoftwareName = br.ReadUInt32();
            o.FormatLogicalBlockSize = br.ReadUInt16();
            o.SoftwareVendorID = br.ReadUInt16();
            o.MediaDate = MTF_DATE_TIME.Parse(br);
            o.MTFMajorVersion = br.ReadByte();
            return o;
        }
        public static MTF_DB_HDR ReadCBH(Stream si) {
            MTF_DB_HDR o = new MTF_DB_HDR();
            BinaryReader br = new BinaryReader(si);
            o.offset = si.Position;
            o.DBLKType = br.ReadUInt32();
            o.BlockAttributes = br.ReadUInt32();
            o.OffsetToFirstEvent = br.ReadUInt16();
            o.OSID = br.ReadByte();
            o.OSVersion = br.ReadByte();
            o.DisplayableSize = br.ReadUInt64();
            o.FormatLogicalAddress = br.ReadUInt64();
            o.ReservedforMBC = br.ReadUInt16();
            o.Reserved1 = br.ReadBytes(6);
            o.ControlBlockID = br.ReadUInt32();
            o.Reserved2 = br.ReadBytes(4);
            o.OSSpecificData = br.ReadUInt32();
            o.StringType = br.ReadByte();
            o.Reserved3 = br.ReadByte();
            o.HeaderChecksum = br.ReadUInt16();
            return o;
        }
        public static MTF_SFMB ReadSfmb(Stream si) {
            MTF_SFMB o = new MTF_SFMB();
            BinaryReader br = new BinaryReader(si);
            o.NumberofFilemarkEntries = br.ReadUInt32();
            o.FilemarkEntriesUsed = br.ReadUInt32();

            int cx = Convert.ToInt32(o.FilemarkEntriesUsed);
            o.PBAofPreviousFilemarksArray = new uint[cx];
            for (int x = 0; x < cx; x++) {
                o.PBAofPreviousFilemarksArray[x] = br.ReadUInt32();
            }
            return o;
        }
        public static MTF_SSET ReadSSet(Stream si) {
            MTF_SSET o = new MTF_SSET();
            BinaryReader br = new BinaryReader(si);
            o.offset = si.Position;
            o.SSETAttributes = br.ReadUInt32();
            o.PasswordEncryptionAlgorithm = br.ReadUInt16();
            o.SoftwareCompressionAlgorithm = br.ReadUInt16();
            o.SoftwareVendorID = br.ReadUInt16();
            o.DataSetNumber = br.ReadUInt16();
            o.DataSetName = br.ReadUInt32();
            o.SetDescription = br.ReadUInt32();
            o.DataSetPassword = br.ReadUInt32();
            o.UserName = br.ReadUInt32();
            o.PhysicalBlockAddress = br.ReadUInt64();
            o.MediaWriteDate = MTF_DATE_TIME.Parse(br);
            o.SoftwareMajorVersion = br.ReadByte();
            o.SoftwareMinorVersion = br.ReadByte();
            o.TimeZone = br.ReadSByte();
            o.MTFMinorVersion = br.ReadByte();
            o.MediaCatalogVersion = br.ReadByte();
            return o;
        }
        public static MTF_VOLB ReadVolb(Stream si) {
            MTF_VOLB o = new MTF_VOLB();
            BinaryReader br = new BinaryReader(si);
            o.offset = si.Position;
            o.VOLBAttributes = br.ReadUInt32();
            o.DeviceName = br.ReadUInt32();
            o.VolumeName = br.ReadUInt32();
            o.MachineName = br.ReadUInt32();
            o.MediaWriteDate = MTF_DATE_TIME.Parse(br);
            return o;
        }
        public static MTF_DIRB ReadDirb(Stream si) {
            MTF_DIRB o = new MTF_DIRB();
            BinaryReader br = new BinaryReader(si);
            o.offset = si.Position;
            o.DIRBAttributes = br.ReadUInt32();
            o.LastModificationDate = MTF_DATE_TIME.Parse(br);
            o.CreationDate = MTF_DATE_TIME.Parse(br);
            o.BackupDate = MTF_DATE_TIME.Parse(br);
            o.LastAccessDate = MTF_DATE_TIME.Parse(br);
            o.DirectoryID = br.ReadUInt32();
            o.DirectoryName = br.ReadUInt32();
            return o;
        }
        public static MTF_FILE ReadFile(Stream si) {
            MTF_FILE o = new MTF_FILE();
            BinaryReader br = new BinaryReader(si);
            o.offset = si.Position;
            o.FILEAttributes = br.ReadUInt32();
            o.LastModificationDate = MTF_DATE_TIME.Parse(br);
            o.CreationDate = MTF_DATE_TIME.Parse(br);
            o.BackupDate = MTF_DATE_TIME.Parse(br);
            o.LastAccessDate = MTF_DATE_TIME.Parse(br);
            o.DirectoryID = br.ReadUInt32();
            o.FileID = br.ReadUInt32();
            o.FileName = br.ReadUInt32();
            return o;
        }
        public static MTF_STREAM_HDR ReadSH(Stream si) {
            MTF_STREAM_HDR o = new MTF_STREAM_HDR();
            BinaryReader br = new BinaryReader(si);
            o.offset = si.Position;
            o.StreamID = br.ReadUInt32();
            o.StreamFileSystemAttributes = br.ReadUInt16();
            o.StreamMediaFormatAttributes = br.ReadUInt16();
            o.StreamLength = br.ReadUInt64();
            o.DataEncryptionAlgorithm = br.ReadUInt16();
            o.DataCompressionAlgorithm = br.ReadUInt16();
            o.Checksum = br.ReadUInt16();
            o.offsetStream = si.Position;
            return o;
        }
        public static MTF_FDD_HDR ReadfddHDR(Stream si) {
            MTF_FDD_HDR o = new MTF_FDD_HDR();
            BinaryReader br = new BinaryReader(si);
            o.offset = si.Position;
            o.LENGTH = br.ReadUInt16();
            o.TYPE = br.ReadUInt32();
            o.MEDIA_SEQ_NUMBER = br.ReadUInt16();
            o.COMMON_BLOCK_ATTRIBUTES = br.ReadUInt32();
            o.FORMAT_LOGICAL_ADDRESS = br.ReadUInt64();
            o.DISPLAYABLE_SIZE = br.ReadUInt64();
            o.LINK = br.ReadUInt32();
            o.OS_ID = br.ReadByte();
            o.OS_VERSION = br.ReadByte();
            o.STRING_TYPE = br.ReadByte();
            o.PAD = br.ReadByte();
            return o;
        }
        public static MTF_FDD_FILE readfddFile(Stream si) {
            MTF_FDD_FILE o = new MTF_FDD_FILE();
            BinaryReader br = new BinaryReader(si);
            o.offset = si.Position;
            o.LastModificationDate = MTF_DATE_TIME.Parse(br);
            o.CreationDate = MTF_DATE_TIME.Parse(br);
            o.BackupDate = MTF_DATE_TIME.Parse(br);
            o.LastAccessDate = MTF_DATE_TIME.Parse(br);
            o.FILEAttributes = br.ReadUInt32();
            o.FileName = br.ReadUInt32();
            o.OS_SPECIFIC_DATA = br.ReadUInt32();
            return o;
        }
        public static MTF_FDD_DIRB readfddDirb(Stream si) {
            MTF_FDD_DIRB o = new MTF_FDD_DIRB();
            BinaryReader br = new BinaryReader(si);
            o.offset = si.Position;
            o.LastModificationDate = MTF_DATE_TIME.Parse(br);
            o.CreationDate = MTF_DATE_TIME.Parse(br);
            o.BackupDate = MTF_DATE_TIME.Parse(br);
            o.LastAccessDate = MTF_DATE_TIME.Parse(br);
            o.DIRBAttributes = br.ReadUInt32();
            o.DirectoryName = br.ReadUInt32();
            o.OS_SPECIFIC_DATA = br.ReadUInt32();
            return o;
        }
        public static MTF_FDD_VOLB readfddVolb(Stream si) {
            MTF_FDD_VOLB o = new MTF_FDD_VOLB();
            BinaryReader br = new BinaryReader(si);
            o.offset = si.Position;
            o.VOLBAttributes = br.ReadUInt32();
            o.DeviceName = br.ReadUInt32();
            o.VolumeName = br.ReadUInt32();
            o.MachineName = br.ReadUInt32();
            o.OS_SPECIFIC_DATA = br.ReadUInt32();
            o.MediaWriteDate = MTF_DATE_TIME.Parse(br);
            return o;
        }
    }

    public class MTF_FDD_VOLB {
        public uint VOLBAttributes;
        public uint DeviceName; // MTF_TAPE_ADDRESS 4 bytes
        public uint VolumeName; // MTF_TAPE_ADDRESS 4 bytes
        public uint MachineName; // MTF_TAPE_ADDRESS 4 bytes
        public uint OS_SPECIFIC_DATA; // MTF_TAPE_ADDRESS 4 bytes
        public DateTime MediaWriteDate; // MTF_DATE_TIME 5 bytes

        public Int64 offset;

        public VOLBAtts VOLBAtts { get { return (VOLBAtts)VOLBAttributes; } }
    }

    public class MTF_FDD_DIRB {
        public DateTime LastModificationDate; // MTF_DATE_TIME 5 bytes
        public DateTime CreationDate; // MTF_DATE_TIME 5 bytes
        public DateTime BackupDate; // MTF_DATE_TIME 5 bytes
        public DateTime LastAccessDate; // MTF_DATE_TIME 5 bytes
        public uint DIRBAttributes; // UINT32 4 bytes
        public uint DirectoryName; // MTF_TAPE_ADDRESS 4 bytes
        public uint OS_SPECIFIC_DATA; // MTF_TAPE_ADDRESS 4 bytes

        public Int64 offset;

        public DIRBAtts DIRBAtts { get { return (DIRBAtts)DIRBAttributes; } }
    }

    public class MTF_FDD_FILE {
        public DateTime LastModificationDate; // MTF_DATE_TIME 5 bytes
        public DateTime CreationDate; // MTF_DATE_TIME 5 bytes
        public DateTime BackupDate; // MTF_DATE_TIME 5 bytes
        public DateTime LastAccessDate; // MTF_DATE_TIME 5 bytes
        public uint FILEAttributes; // UINT32 4 bytes
        public uint FileName; // MTF_TAPE_ADDRESS 4 bytes
        public uint OS_SPECIFIC_DATA; // MTF_TAPE_ADDRESS 4 bytes

        public Int64 offset;

        public FILEAtts FILEAtts { get { return (FILEAtts)FILEAttributes; } }
    }

    public class MTF_FDD_HDR {
        public ushort LENGTH;
        public uint TYPE;
        public ushort MEDIA_SEQ_NUMBER;
        public uint COMMON_BLOCK_ATTRIBUTES;
        public ulong FORMAT_LOGICAL_ADDRESS;
        public ulong DISPLAYABLE_SIZE;
        public uint LINK;
        public byte OS_ID;
        public byte OS_VERSION;
        public byte STRING_TYPE;
        public byte PAD;

        public Int64 offset;

        public Strty Strty { get { return (Strty)STRING_TYPE; } }

        public string TYPEText {
            get {
                return Encoding.ASCII.GetString(new byte[] { 
                    (byte)(TYPE >> 0), 
                    (byte)(TYPE >> 8), 
                    (byte)(TYPE >> 16), 
                    (byte)(TYPE >> 24), 
                });
            }
        }
    }

    [Flags]
    public enum StreamMediaFormatAtts {
        CONTINUE = 1,
        VARIABLE = 2,
        VAR_END = 4,
        ENCRYPTED = 8,
        COMPRESSED = 16,
        CHECKSUMED = 32,
        EMBEDDED_LENGTH = 64,
    }

    public class MTF_STREAM_HDR {
        public uint StreamID;
        public ushort StreamFileSystemAttributes;
        public ushort StreamMediaFormatAttributes;
        public ulong StreamLength;
        public ushort DataEncryptionAlgorithm;
        public ushort DataCompressionAlgorithm;
        public ushort Checksum;

        public Int64 offset, offsetStream;

        public StreamMediaFormatAtts StreamMediaFormatAtts { get { return (StreamMediaFormatAtts)StreamMediaFormatAttributes; } }

        public string StreamIDText {
            get {
                return Encoding.ASCII.GetString(new byte[] { 
                    (byte)(StreamID >> 0), 
                    (byte)(StreamID >> 8), 
                    (byte)(StreamID >> 16), 
                    (byte)(StreamID >> 24), 
                });
            }
        }

        public ushort ComputedChecksum {
            get {
                MemoryStream os = new MemoryStream(20);
                BinaryWriter wr = new BinaryWriter(os);
                wr.Write(StreamID);
                wr.Write(StreamFileSystemAttributes);
                wr.Write(StreamMediaFormatAttributes);
                wr.Write(StreamLength);
                wr.Write(DataEncryptionAlgorithm);
                wr.Write(DataCompressionAlgorithm);
                os.Position = 0;
                UInt16 v = 0;
                BinaryReader br = new BinaryReader(os);
                for (int x = 0; x < 10; x++) v ^= br.ReadUInt16();
                return v;
            }
        }
    }

    [Flags]
    public enum FILEAtts {
        READ_ONLY_BIT = 256,
        HIDDEN_BIT = 512,
        SYSTEM_BIT = 1024,
        MODIFIED_BIT = 2048,
        IN_USE_BIT = 1 << 16,
        NAME_IN_STREAM_BIT = 1 << 17,
        CORRUPT_BIT = 1 << 18,
    }

    public class MTF_FILE {
        public uint FILEAttributes;
        public DateTime LastModificationDate; // MTF_DATE_TIME 5 bytes
        public DateTime CreationDate; // MTF_DATE_TIME 5 bytes
        public DateTime BackupDate; // MTF_DATE_TIME 5 bytes
        public DateTime LastAccessDate;// MTF_DATE_TIME 5 bytes
        public uint DirectoryID; // UINT32 4 bytes
        public uint FileID; // UINT32 4 bytes
        public uint FileName; // MTF_TAPE_ADDRESS 4 bytes

        public Int64 offset;

        public FILEAtts FILEAtts { get { return (FILEAtts)FILEAttributes; } }
    }

    [Flags]
    public enum DIRBAtts {
        READ_ONLY_BIT = 256,
        HIDDEN_BIT = 512,
        SYSTEM_BIT = 1024,
        MODIFIED_BIT = 2048,
        EMPTY_BIT = 65536,
        PATH_IN_STREAM_BIT = 1 << 17,
        CORRUPT_BIT = 1 << 18,
    }

    public class MTF_DIRB {
        public uint DIRBAttributes; // UINT32 4 bytes
        public DateTime LastModificationDate; // MTF_DATE_TIME 5 bytes
        public DateTime CreationDate;// MTF_DATE_TIME 5 bytes
        public DateTime BackupDate; // MTF_DATE_TIME 5 bytes
        public DateTime LastAccessDate; // MTF_DATE_TIME 5 bytes
        public uint DirectoryID; // UINT32 4 bytes
        public uint DirectoryName; // MTF_TAPE_ADDRESS 4 bytes

        public Int64 offset;

        public DIRBAtts DIRBAtts { get { return (DIRBAtts)DIRBAttributes; } }
    }

    [Flags]
    public enum VOLBAtts {
        NO_REDIRECT_RESTORE_BIT = 1,
        NON_VOLUME_BIT = 2,
        DEV_DRIVE_BIT = 4,
        DEV_UNC_BIT = 8,
        DEV_OS_SPEC_BIT = 16,
        DEV_VEND_SPEC_BIT = 32,
    }

    public class MTF_VOLB {
        public uint VOLBAttributes;
        public uint DeviceName; // MTF_TAPE_ADDRESS 4 bytes
        public uint VolumeName; // MTF_TAPE_ADDRESS 4 bytes
        public uint MachineName; // MTF_TAPE_ADDRESS 4 bytes
        public DateTime MediaWriteDate; // MTF_DATE_TIME 5 bytes

        public Int64 offset;

        public VOLBAtts VOLBAtts { get { return (VOLBAtts)VOLBAttributes; } }
    }

    [Flags]
    public enum SSETAtts {
        TRANSFER_BIT = 1,
        COPY_BIT = 2,
        NORMAL_BIT = 4,
        DIFFERENTIAL_BIT = 8,
        INCREMENTAL_BIT = 16,
        DAILY_BIT = 32,
    }

    public class MTF_SSET {
        public uint SSETAttributes;
        public ushort PasswordEncryptionAlgorithm;
        public ushort SoftwareCompressionAlgorithm;
        public ushort SoftwareVendorID;
        public ushort DataSetNumber;
        public uint DataSetName; // MTF_TAPE_ADDRESS 4 bytes
        public uint SetDescription; // MTF_TAPE_ADDRESS 4 bytes
        public uint DataSetPassword; // MTF_TAPE_ADDRESS 4 bytes
        public uint UserName; // MTF_TAPE_ADDRESS 4 bytes
        public ulong PhysicalBlockAddress; // (PBA) UINT64 8 bytes
        public DateTime MediaWriteDate; // MTF_DATE_TIME 5 bytes
        public byte SoftwareMajorVersion; // UINT8 1 byte
        public byte SoftwareMinorVersion; // UINT8 1 byte
        public sbyte TimeZone; // INT8 1 byte
        public byte MTFMinorVersion; // UINT8 1 byte
        public byte MediaCatalogVersion; // UINT8 1 byte.

        public Int64 offset;

        public SSETAtts SSETAtts { get { return (SSETAtts)SSETAttributes; } }
    }

    public class MTF_SFMB {
        public UInt32 NumberofFilemarkEntries;
        public UInt32 FilemarkEntriesUsed;
        public UInt32[] PBAofPreviousFilemarksArray;
    }

    public class MTF_DATE_TIME {
        public static DateTime Parse(BinaryReader br) {
            UInt64 v = (((UInt64)br.ReadByte()) << 32);
            v |= (((UInt64)br.ReadByte()) << 24);
            v |= (((UInt64)br.ReadByte()) << 16);
            v |= (((UInt64)br.ReadByte()) << 8);
            v |= (((UInt64)br.ReadByte()) << 0);
            if (v == 0)
                return DateTime.MinValue;
            return new DateTime(
                ((int)(v >> 26)) & 0x3FFF, // yyyy, shift0, 14 bits
                ((int)(v >> 22)) & 0xF, // MM, shift14, 4 bits
                ((int)(v >> 17)) & 0x1F, // dd, shift18, 5 bits
                ((int)(v >> 12)) & 0x1F, // hh, shift23, 5 bits
                ((int)(v >> 6)) & 0x3F, // mm, shift28, 6 bits
                ((int)(v >> 0)) & 0x3F // ss, shift34, 6 bits
                );
        }
    }

    [Flags]
    public enum TapeAtts {
        SOFT_FILEMARK = 1,
        MEDIA_LABEL = 2,
    }

    public enum MBCTy {
        None = 0,
        Type1MBC = 1,
        Type2MBC = 2,
    }

    public class MTF_TAPE {
        public UInt32 MediaFamilyID;
        public UInt32 TAPEAttributes;
        public UInt16 MediaSequenceNumber;
        public UInt16 PasswordEncryptionAlgorithm;
        public UInt16 SoftFilemarkBlockSize;
        public UInt16 MediaBasedCatalogType;
        public UInt32 MediaName;// MTF_TAPE_ADDRESS
        public UInt32 MediaDescriptionMediaLabel; // MTF_TAPE_ADDRESS
        public UInt32 MediaPassword; // MTF_TAPE_ADDRESS 
        public UInt32 SoftwareName; // MTF_TAPE_ADDRESS 4 bytes
        public UInt16 FormatLogicalBlockSize;
        public UInt16 SoftwareVendorID;
        public DateTime MediaDate; // 5 bytes
        public byte MTFMajorVersion;

        public Int64 offset;

        public TapeAtts TapeAtts { get { return (TapeAtts)TAPEAttributes; } }
        public MBCTy MBCTy { get { return (MBCTy)MediaBasedCatalogType; } }
    }

    [Flags]
    public enum BlockAtts {
        CONTINUATION = 1,
        COMPRESSION = 1 << 2,
        EOS_AT_EOM = 1 << 3,
        SET_MAP_EXISTS = 1 << 16,
        FDD_ALLOWED = 1 << 17,
        FDD_EXISTS = 1 << 16,
        ENCRYPTION = 1 << 17,
        FDD_ABORTED = 1 << 16,
        END_OF_FAMILY = 1 << 17,
        ABORTED_SET = 1 << 18,
        NO_ESET_PBA = 1 << 16,
        INVALID_ESET_PBA = 1 << 17,
    }

    public enum Strty {
        None = 0,
        ANSI = 1,
        UNICODE = 2,
    }

    public class MTF_DB_HDR {
        public uint DBLKType;
        public uint BlockAttributes;
        public ushort OffsetToFirstEvent;
        public byte OSID;
        public byte OSVersion;
        public ulong DisplayableSize;
        public ulong FormatLogicalAddress;
        public ushort ReservedforMBC;
        public byte[] Reserved1; // 6 bytes
        public uint ControlBlockID;
        public byte[] Reserved2; // 4 bytes
        public uint OSSpecificData; // 4 bytes, MTF_TAPE_ADDRESS
        public byte StringType;
        public byte Reserved3;
        public ushort HeaderChecksum;

        public Int64 offset;

        public Strty Strty { get { return (Strty)StringType; } }

        public BlockAtts BlockAtts { get { return (BlockAtts)BlockAttributes; } }

        public string DBLKName {
            get {
                return Encoding.ASCII.GetString(new byte[] { 
                    (byte)(DBLKType >> 0), 
                    (byte)(DBLKType >> 8), 
                    (byte)(DBLKType >> 16), 
                    (byte)(DBLKType >> 24), 
                });
            }
        }

        public ushort ComputedChecksum {
            get {
                MemoryStream os = new MemoryStream(50);
                BinaryWriter wr = new BinaryWriter(os);
                wr.Write(DBLKType);
                wr.Write(BlockAttributes);
                wr.Write(OffsetToFirstEvent);
                wr.Write(OSID);
                wr.Write(OSVersion);
                wr.Write(DisplayableSize);
                wr.Write(FormatLogicalAddress);
                wr.Write(ReservedforMBC);
                wr.Write(Reserved1);
                wr.Write(ControlBlockID);
                wr.Write(Reserved2);
                wr.Write(OSSpecificData);
                wr.Write(StringType);
                wr.Write(Reserved3);
                os.Position = 0;
                UInt16 v = 0;
                BinaryReader br = new BinaryReader(os);
                for (int x = 0; x < 25; x++) v ^= br.ReadUInt16();
                return v;
            }
        }
    }

    public class DBLk {

        public MTF_DB_HDR hdr;
        public List<MTF_STREAM_HDR> alsh = new List<MTF_STREAM_HDR>();
    }
}
